#include <QDebug>

#include "qlsavplayer.h"

#define SDL_AUDIO_BUFFER_SIZE 1024
#define AVCODEC_AVCODEC_MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audio
#define FLUSH_DATA "FLUSH"

typedef unsigned long DWORD;
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned long ULONG_PTR;
typedef ULONG_PTR DWORD_PTR;

#define MAKEWORD(a, b)	\
	((WORD)(((BYTE)(((DWORD_PTR)(a)) & 0xff)) | \
	((WORD)((BYTE)(((DWORD_PTR)(b)) & 0xff))) << 8))
#define LOWORD(l)		((WORD)(((DWORD_PTR)(l)) & 0xffff))
#define LOBYTE(w)		((BYTE)(((DWORD_PTR)(w)) & 0xff))
#define HIBYTE(w)		((BYTE)((((DWORD_PTR)(w)) >> 8) & 0xff))

/* 根据解码后的视频数据 计算出视频的pts */
double SynchronizeVideo(QLsAVParam *avParam, AVFrame *srcFrame, double pts)
{
	double frameDelay;

	/* 如果有pts，设置视频时钟 */
	if(pts != 0) avParam->m_vClock = pts;
	/* 如果没有pts，获取视频时钟 */
	else pts = avParam->m_vClock;

	/* 更新视频时钟 */
	frameDelay = av_q2d(avParam->m_vStream->time_base);
	/* 如果重复一个帧，相应地调整时钟 */
	frameDelay += srcFrame->repeat_pict * (frameDelay * 0.5);
	avParam->m_vClock += frameDelay;
	return pts;
}

/* 音量调节 */
void RaiseVolume(char* buf, int size, int uRepeat, double vol)
{
	if (!size) return;

	for (int i = 0; i < size; i += 2)
	{
		short wData = MAKEWORD(buf[i], buf[i + 1]);
		long dwData = wData;
		for (int j = 0; j < uRepeat; j++)
		{
			dwData = dwData * vol;
			if (dwData < -0x8000) dwData = -0x8000;
			else if (dwData > 0x7FFF) dwData = 0x7FFF;
		}
		wData = LOWORD(dwData);
		buf[i] = LOBYTE(wData);
		buf[i + 1] = HIBYTE(wData);
	}
}

/* 视频解码线程 */
int VDecodeThread(void *arg)
{
	QLsAVParam *avParam = (QLsAVParam *)arg;
	AVPacket pkt1, *packet = &pkt1;
	double video_pts = 0;	/* 当前视频的pts */
	double audio_pts = 0;	/* 音频pts */

	/* 参数初始化 */
	avParam->m_bVDecodeThreadFinished = false;

	/* 解码视频相关 */
	AVFrame *pFrame = av_frame_alloc();
	AVFrame *pFrameRGB = av_frame_alloc();
	/* 解码后的rgb数据 */
	uint8_t *out_buffer_rgb = (uint8_t *)av_malloc(
				av_image_get_buffer_size(AV_PIX_FMT_RGB32, avParam->m_vCodecCtx->width,
								   avParam->m_vCodecCtx->height, 1) * sizeof(uint8_t));
	/* 用于解码后的视频格式转换，将解码后的YUV数据转换成RGB32 */
	SwsContext *img_convert_ctx = sws_getContext(
				avParam->m_vCodecCtx->width, avParam->m_vCodecCtx->height,
				avParam->m_vCodecCtx->pix_fmt, avParam->m_vCodecCtx->width,
				avParam->m_vCodecCtx->height, AV_PIX_FMT_RGB32, SWS_BICUBIC, nullptr, nullptr, nullptr);
	av_image_fill_arrays(pFrameRGB->data, pFrameRGB->linesize, out_buffer_rgb, AV_PIX_FMT_RGB32,
				   avParam->m_vCodecCtx->width, avParam->m_vCodecCtx->height, 1);

	bool bFrist = avParam->m_bFirstPause;
	for(;;)
	{	/* 停止 */
		if(avParam->m_bQuit)
		{
			/* 清空队列 */
			if(avParam->m_vPktQueue)
			{
				avParam->m_vPktQueue->Clear();
			}
			break;
		}

		/* 暂停 */
		if(avParam->m_bPause == true)
		{ SDL_Delay(10); continue; }

		/* 从队列中读取数据包 */
		if(!avParam->m_vPktQueue->PopFront(packet))
		{
			/* 队列里面没有数据了 且 解码完毕了 */
			if(avParam->m_bDecodeFinished) break;
			/* 队列只是暂时没有数据而已 */
			else { SDL_Delay(1); continue; }
		}

		/* 收到这个数据 说明刚刚执行过跳转 现在需要把解码器的数据 清除一下 */
		if(strcmp((char*)packet->data, FLUSH_DATA) == 0)
		{
			avcodec_flush_buffers(avParam->m_vCodecCtx);
			av_packet_unref(packet);
			continue;
		}

		/* 向解码器中添加一包输入数据 */
		if (avcodec_send_packet(avParam->m_vCodecCtx, packet))
		{
			qDebug() << "input AVPacket to decoder failed!";
			av_packet_unref(packet);
			continue;
		}

		/* 从解码器中获取已解码的输出数据 */
		while(!avcodec_receive_frame(avParam->m_vCodecCtx, pFrame))
		{	/* 获取视频pts */
			if (packet->dts == AV_NOPTS_VALUE && pFrame->opaque &&
					*(qint64*)pFrame->opaque != AV_NOPTS_VALUE)
				video_pts = *(uint64_t *)pFrame->opaque;
			else if (packet->dts != AV_NOPTS_VALUE)
				video_pts = packet->dts;
			else video_pts = 0;

			video_pts *= av_q2d(avParam->m_vStream->time_base);
			video_pts = SynchronizeVideo(avParam, pFrame, video_pts);

			/* 发生了跳转 则跳过关键帧到目的时间的这几帧 */
			if (avParam->m_bVSeek)
			{
				if (video_pts < avParam->m_seekTime)
				{
					av_packet_unref(packet);
					continue;
				}
				else avParam->m_bVSeek = false;
			}

			/* 音视频同步 */
			while(1)
			{
				/* 退出 */
				if (avParam->m_bQuit) break;

				/* 有音频 */
				if (avParam->m_aStream && !avParam->m_bADecodeThreadFinished)
				{	/* 解码完了 且 音频数据也播放完了 就剩下视频数据了 直接显示出来了 不用同步了 */
					if (avParam->m_bDecodeFinished && avParam->m_aPktQueue->m_size == 0)
						break;

					/**
					 * 有音频的情况下，将视频同步到音频
					 * 跟音频的pts做对比，比视频快则做延时
					 */
					audio_pts = avParam->m_aClock;
				}
				/** 没有音频的情况下，直接同步到外部时钟 */
				else
				{
					audio_pts = (av_gettime() - avParam->m_startTime) / 1e6;
					avParam->m_aClock = audio_pts;
				}

				//主要是 跳转的时候 我们把video_clock设置成0了
				//因此这里需要更新video_pts
				//否则当从后面跳转到前面的时候 会卡在这里
				video_pts = avParam->m_vClock;

				/* 视频播放比音频快，视频等待音频，即音视频同步 */
				if (video_pts <= audio_pts)
					break;
				int delayTime = (video_pts - audio_pts) * 1000;
				delayTime = qMin(delayTime, 5);
				SDL_Delay(delayTime);
			}

			sws_scale(img_convert_ctx, (const uint8_t * const *)pFrame->data, pFrame->linesize, 0,
					  avParam->m_vCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);

			/**
			 * QImage构造函数 传进去的数据，他只是把指针指向了而已，
			 * 如果数据在外部释放了，那么调用QImage就会出问题了，
			 * 所以需要一次copy，copy是深度拷贝。
			 *
			 * 去掉透明的部分 有些奇葩的视频会透明
			 */
			QImage tmpImg((uchar *)out_buffer_rgb,avParam->m_vCodecCtx->width,avParam->m_vCodecCtx->height,QImage::Format_RGB32);
//			QImage image = tmpImg.copy();
			QImage image = tmpImg.convertToFormat(QImage::Format_RGB888,Qt::NoAlpha);
			avParam->m_avPlayer->DisplayVideo(image); //调用激发信号的函数

			if(bFrist)
			{
				avParam->m_avPlayer->Pause();
				bFrist = false;
			}
		}

		av_packet_unref(packet);

	}

	av_free(pFrame);
	av_free(pFrameRGB);
	av_free(out_buffer_rgb);

	sws_freeContext(img_convert_ctx);

	avParam->m_bQuit = true;
	avParam->m_bVDecodeThreadFinished = true;

	qDebug() << __FUNCTION__ << "end";

	return 0;
}

/* 音频回调函数 */
void ACallback(void *userdata, Uint8 *stream, int len)
{
	QLsAVParam *avParam = (QLsAVParam *)userdata;

	int len1, audio_data_size;

	/* len是由SDL传入的SDL缓冲区的大小，如果这个缓冲未满，我们就一直往里填充数据 */
	while(len > 0)
	{
		/* 没有解码出的数据，从数据包队列中解码出新的数据 */
		if(avParam->audio_buf_index >= avParam->audio_buf_size)
		{	/* 解码新的数据 */
			audio_data_size = ADecode(avParam);

			if (avParam->audio_buf == nullptr) return;

			/* 没有解码出数据，默认播放静音 */
			if(audio_data_size < 0)
			{
				/* silence */
				avParam->audio_buf_size = 1024;
				/* 清零，静音 */
				memset(avParam->audio_buf, 0, avParam->audio_buf_size);
			}
			else avParam->audio_buf_size = audio_data_size;
			avParam->audio_buf_index = 0;
		}

		if (avParam->audio_buf == nullptr) return;

		/*  查看stream可用空间，决定一次copy多少数据，剩下的下次继续copy */
		len1 = qMin(avParam->audio_buf_size - avParam->audio_buf_index, (quint32)len);

		/* 静音 */
		if (avParam->m_bMute)
			memset(avParam->audio_buf + avParam->audio_buf_index, 0, len1);
		/* 调节音量 */
		else RaiseVolume((char*)avParam->audio_buf + avParam->audio_buf_index,
						 len1, 1, avParam->m_volume);

		/* 填充SDL缓冲区，用于音频流播放 */
		memcpy(stream, avParam->audio_buf + avParam->audio_buf_index, len1);

		len -= len1;
		stream += len1;
		avParam->audio_buf_index += len1;
	}
}

/* 音频解码函数 */
int ADecode(QLsAVParam *avParam)
{
	AVPacket *packet = &avParam->m_aPacket;	/* 音频数据 */

	int len2, decoded_data_size;
	qint64 dec_channel_layout;
	int wanted_nb_samples, resampled_data_size;

	for (;;)
	{
		/* ----- 操作响应 ----- */
		/* 停止 */
		if(avParam->m_bQuit)
		{
			avParam->m_bADecodeThreadFinished = true;
			avParam->m_aPktQueue->Clear();
			return -1;
		}

		/* 暂停 */
		if(avParam->m_bPause == true) return -1;

		/* ----- 音频数据包解析 ----- */
		/* 从队列里取出音频数据包 */
		if(!avParam->m_aPktQueue->PopFront(packet)) return -1;

		/* 更新音频时钟 */
		if(packet->pts != AV_NOPTS_VALUE)
			avParam->m_aClock = av_q2d(avParam->m_aStream->time_base) * packet->pts;

		/* 清除音频解码器数据 */
		if(strcmp((char*)packet->data, FLUSH_DATA) == 0)
		{
			avcodec_flush_buffers(avParam->m_aCodecCtx);
			av_packet_unref(packet);
			continue;
		}

		/* 发生了跳转，则跳过关键帧到目的时间的这几帧 */
		if(avParam->m_bASeek)
		{
			if(avParam->m_aClock < avParam->m_seekTime) break;
			else avParam->m_bASeek = false;
		}

		/* ----- 解码音频数据 ----- */
		/* 解码一个音频数据包 */
		if (avcodec_send_packet(avParam->m_aCodecCtx, packet))
		{	/* 解码出错，跳过这帧数据 */
			qDebug() << "m_aCodecCtx input AVPacket to decoder failed!";
			av_packet_unref(packet);
			continue;
		}

		/* 解码一包中的一帧或多帧数据 */
		resampled_data_size = 0;
		while(!avcodec_receive_frame(avParam->m_aCodecCtx, avParam->m_aFrame))
		{
			/* 计算解码出来的桢需要的缓冲大小 */
			decoded_data_size = av_samples_get_buffer_size(
						nullptr,
						avParam->m_aFrame->channels, avParam->m_aFrame->nb_samples,
						(AVSampleFormat)avParam->m_aFrame->format, 1);

			/* 解码后的音频格式转换 */
			dec_channel_layout = (
						avParam->m_aFrame->channel_layout &&
						avParam->m_aFrame->channels ==
						av_get_channel_layout_nb_channels(avParam->m_aFrame->channel_layout)
						) ?
						avParam->m_aFrame->channel_layout :
						av_get_default_channel_layout(avParam->m_aFrame->channels);
			wanted_nb_samples = avParam->m_aFrame->nb_samples;
			if(avParam->m_aFrame->format != avParam->audio_src_fmt ||
					dec_channel_layout != avParam->audio_src_channel_layout ||
					avParam->m_aFrame->sample_rate != avParam->audio_src_freq ||
					(wanted_nb_samples != avParam->m_aFrame->nb_samples && !avParam->m_aSwrCtx))
			{
				if(avParam->m_aSwrCtx) swr_free(&avParam->m_aSwrCtx);

				/* 音频播放参数固定 */
				avParam->m_aSwrCtx = swr_alloc();
				avParam->m_aSwrCtx = swr_alloc_set_opts(
							avParam->m_aSwrCtx, avParam->audio_tgt_channel_layout,
							(AVSampleFormat)avParam->audio_tgt_fmt,
							avParam->audio_tgt_freq, dec_channel_layout,
							(AVSampleFormat)avParam->m_aFrame->format,
							avParam->m_aFrame->sample_rate, 0, nullptr);

				/* 使用指定的参数打开重采样器 */
				if(!avParam->m_aSwrCtx || swr_init(avParam->m_aSwrCtx) < 0)
				{ qDebug() << "swr_init() failed"; break; }

				avParam->audio_src_channel_layout = dec_channel_layout;
				avParam->audio_src_channels = avParam->m_aStream->codecpar->channels;
				avParam->audio_src_freq = avParam->m_aStream->codecpar->sample_rate;
				avParam->audio_src_fmt = (AVSampleFormat)avParam->m_aStream->codecpar->format;
			}

			/* 这里我们可以对采样数进行调整，增加或者减少，一般可以用来做声画同步 */
			if(avParam->m_aSwrCtx)
			{
				const uint8_t **in = (const uint8_t **)avParam->m_aFrame->extended_data;
				uint8_t *out[] = { avParam->audio_buf2 };

				if(wanted_nb_samples != avParam->m_aFrame->nb_samples)
				{	/* 补偿 */
					if(swr_set_compensation(
								avParam->m_aSwrCtx,
								(wanted_nb_samples - avParam->m_aFrame->nb_samples) *
								avParam->audio_tgt_freq / avParam->m_aFrame->sample_rate,
								wanted_nb_samples * avParam->audio_tgt_freq /
								avParam->m_aFrame->sample_rate) < 0)
					{ qDebug() << __FUNCTION__ << "swr_set_compensation() failed"; }
				}

				len2 = swr_convert(
							avParam->m_aSwrCtx, out,
							sizeof(avParam->audio_buf2) / avParam->audio_tgt_channels /
							av_get_bytes_per_sample(avParam->audio_tgt_fmt),
							in, avParam->m_aFrame->nb_samples);

				if (len2 < 0)
				{ qDebug() << __FUNCTION__ << "swr_convert() failed"; break; }
				if (len2 == (int)sizeof(avParam->audio_buf2) / avParam->audio_tgt_channels /
						av_get_bytes_per_sample(avParam->audio_tgt_fmt))
					swr_init(avParam->m_aSwrCtx);		/* 使用指定的参数打开重采样器 */

				avParam->audio_buf = avParam->audio_buf2;
				resampled_data_size += len2 * avParam->audio_tgt_channels *
						av_get_bytes_per_sample(avParam->audio_tgt_fmt);
			}
			else
			{
				resampled_data_size += decoded_data_size;
				avParam->audio_buf = avParam->m_aFrame->data[0];
			}
		}

		/* 释放音频数据包缓存 */
		av_packet_unref(packet);

		/* 返回解码出的数据长度 */
		return resampled_data_size;
	}

	if (packet->data)
		av_packet_unref(packet);
	memset(packet, 0, sizeof(*packet));

	avParam->audio_pkt_data = packet->data;
	avParam->audio_pkt_size = packet->size;

	/* 返回解码出的数据长度 */
	return 0;
}

/* 音视频处理参数封装 */
QLsAVParam::QLsAVParam(QLsAVPlayer *avPlayer)
	: m_avPlayer(avPlayer)				/* 音频播放器 */
	, m_bFirstPause(true)				/* 第一次显示是否暂停(为了加载完视频有个预览界面) */
	, m_volume(1)						/* 音量 [0, 1] */
{
}

QLsAVParam::~QLsAVParam()
{
}

/* 参数重置 */
void QLsAVParam::ResetParam()
{
	/* 音视频整体 */
	m_inputCtx = nullptr;
	m_bDecodeThreadFinished = false;
	m_bDecodeFinished = false;
	m_bQuit = false;
	m_bPause = false;
	m_bMute = m_bMute;
	m_bSeek = false;
	m_seekPos = 0;
	m_seekTime = 0;
	m_startTime = 0;
	m_pauseStartTime = 0;
	m_bFirstPause = m_bFirstPause;

	/* 视频 */
	m_vStreamIndex = -1;
	m_vCodecCtx = nullptr;
	m_vCodec = nullptr;
	m_vStream = nullptr;
	if(m_vPktQueue)
	{
		delete m_vPktQueue;
		m_vPktQueue = nullptr;
	}
	if(m_vDecodeThread.isRunning())
		m_vDecodeThread.waitForFinished();
	m_bVDecodeThreadFinished = false;
	m_vClock = 0;
	m_bVSeek = false;

	/* 音频 */
	m_aStreamIndex = -1;
	m_aCodecCtx = nullptr;
	m_aCodec = nullptr;
	m_aStream = nullptr;
	if(m_aPktQueue)
	{
		delete m_aPktQueue;
		m_aPktQueue = nullptr;
	}
	Q_UNUSED(m_aPacket)
	m_bADecodeThreadFinished = false;
	m_aFrame = nullptr;
	m_volume = m_volume;
	m_aClock = 0;
	m_bASeek = false;
	m_aFrameReSample = nullptr;
	m_aSwrCtx = nullptr;
}

/* ----- 音视频解码 ----- */
QLsAVPlayer::QLsAVPlayer(QObject *parent)
	: QObject(parent)
	, m_thread(new QThread)
	, m_avParam(new QLsAVParam(this))
	, m_playerStatus(Player_Stop)
	, m_aDeviceID(-1)
{
	moveToThread(m_thread);
	m_thread->start();

//	av_register_all();				/* 初始化FFMPEG，调用了这个才能正常使用编码器和解码器 */
	avformat_network_init();		/* 支持打开网络文件 */

	/* ---------- 多线程信号槽 ---------- */
	connect(this, &QLsAVPlayer::sigLoad, this, &QLsAVPlayer::slotLoad);
}

QLsAVPlayer::~QLsAVPlayer()
{
	Stop(true);

	m_thread->quit();
	m_thread->wait();
	delete m_thread;
	m_thread = nullptr;

	delete m_avParam;
	m_avParam = nullptr;
}

/* 解码 */
void QLsAVPlayer::Load()
{
//	m_lockDecode.lock();
//	const int nAvail = m_semDecode.available();
//	if(nAvail > 0)
//	{
//		m_semDecode.tryAcquire(nAvail);
//	}
	emit sigLoad();
//	m_semDecode.tryAcquire(1, 5000);
//	m_lockDecode.unlock();
}

/* 显示一帧图像 */
void QLsAVPlayer::DisplayVideo(QImage img)
{
	emit sigGetOneFrame(img);
}

/* 重新播放 */
bool QLsAVPlayer::Replay()
{
	if (m_playerStatus != Player_Stop) return false;

	Load();
	return true;
}

/* 播放 */
bool QLsAVPlayer::Play()
{
	m_avParam->m_bPause = false;

	if (m_playerStatus != Player_Pause) return false;
	m_playerStatus = Player_Playing;
	emit sigStatusChange(Player_Playing);

	return true;
}

/* 暂停 */
bool QLsAVPlayer::Pause()
{
	m_avParam->m_bPause = true;

	if(m_playerStatus != Player_Playing) return false;
	m_avParam->m_pauseStartTime = av_gettime();
	m_playerStatus = Player_Pause;
	emit sigStatusChange(Player_Pause);

	return true;
}

/* 停止 */
bool QLsAVPlayer::Stop(bool bWait)
{
	if(m_playerStatus == Player_Stop) return false;

	m_playerStatus = Player_Stop;
	m_avParam->m_bQuit = true;

	/* 等待解码线程执行完毕再返回 */
	if(bWait) while(!m_avParam->m_bDecodeThreadFinished) SDL_Delay(3);

	return true;
}

/* 跳转(微秒) */
void QLsAVPlayer::Seek(qint64 pos)
{
	if(m_avParam->m_bSeek) return;

	m_avParam->m_seekPos = pos;
	m_avParam->m_bSeek = true;
}

/* 静音 */
bool QLsAVPlayer::mute() const
{
	return m_avParam->m_bMute;
}

void QLsAVPlayer::setMute(const bool &bMute)
{
	m_avParam->m_bMute = bMute;
}

/* 音量 */
void QLsAVPlayer::SetVolume(const float &volume)
{
	m_avParam->m_volume = volume;
}

/* 路径 */
QString QLsAVPlayer::AVUrl() const
{
	return m_AVUrl;
}

void QLsAVPlayer::setAVUrl(const QString &AVUrl)
{
	m_AVUrl = AVUrl;
}

/* 音视频播放状态 */
QLsAVPlayer::QLsPlayerStatus QLsAVPlayer::playerStatus() const
{
	return m_playerStatus;
}

/* 播放进度/当前时间 */
qint64 QLsAVPlayer::curTime() const
{
	return m_avParam->m_aClock;
}

/* 总时长 */
qint64 QLsAVPlayer::totalTime() const
{
	return m_avParam->m_inputCtx->duration;
}

/* 设置是否显示预览(第一次加载视频是否暂停) */
void QLsAVPlayer::setPreview(const bool &bFirstPause)
{
	m_avParam->m_bFirstPause = bFirstPause;
}

/* 解码开始 */
void QLsAVPlayer::DecodeStart()
{
	qDebug() << __FUNCTION__ << QThread::currentThreadId();

	/* SDL初始化需要放入子线程中，否则有些电脑会有问题 */
	if (SDL_Init(SDL_INIT_AUDIO))
	{
		qDebug() << "SDL_INIT_AUDIO error：" << SDL_GetError();
		return;
	}

	/* 重置参数 */
	m_avParam->ResetParam();
}

/* 解码结束 */
void QLsAVPlayer::DecodeEnd()
{
	m_avParam->m_aPktQueue->Clear();
	m_avParam->m_vPktQueue->Clear();

	/* 不是外部调用的 stop 是正常播放结束 */
	if (m_playerStatus != Player_Stop)
		Stop();

	/* 确保音视频线程结束后 再结束解码线程 */
	while((m_avParam->m_vStream && !m_avParam->m_bVDecodeThreadFinished) ||
		  (m_avParam->m_aStream && !m_avParam->m_bADecodeThreadFinished))
		QThread::msleep(10);

	/* SDL：暂停播放、关闭音频处理线程、关闭音频设备 */
	SDLCloseADevice();

	/* 视频 */
	if(m_avParam->m_vStreamIndex >= 0)
	{
		avcodec_close(m_avParam->m_vCodecCtx);
		if(m_avParam->m_vPktQueue)
		{
			delete m_avParam->m_vPktQueue;
			m_avParam->m_vPktQueue = nullptr;
		}
	}

	/* 音频 */
	if(m_avParam->m_aSwrCtx)
		swr_free(&m_avParam->m_aSwrCtx);
	if(m_avParam->m_aFrame)
		av_frame_free(&m_avParam->m_aFrame);
	if(m_avParam->m_aStreamIndex >= 0)
	{
		avcodec_close(m_avParam->m_aCodecCtx);
		if(m_avParam->m_aPktQueue)
		{
			delete m_avParam->m_aPktQueue;
			m_avParam->m_aPktQueue = nullptr;
		}
	}

	avformat_close_input(&m_avParam->m_inputCtx);
	avformat_free_context(m_avParam->m_inputCtx);

	SDL_Quit();

	m_avParam->m_bDecodeThreadFinished = true;

	emit sigStatusChange(Player_Stop);

	qDebug() << __FUNCTION__;
}

/* SDL打开音频设备 */
bool QLsAVPlayer::SDLOpenADevice()
{
	/* SDL音频规范 */
	SDL_AudioSpec wanted_spec, spec;

	/* 双通道 */
	qint64 wanted_channel_layout = av_get_default_channel_layout(2);
	wanted_channel_layout &= ~AV_CH_LAYOUT_STEREO_DOWNMIX;
	wanted_spec.channels = av_get_channel_layout_nb_channels(wanted_channel_layout);

	wanted_spec.freq = 44100;
	wanted_spec.format = AUDIO_S16SYS;
	wanted_spec.silence = 0;						/* 静音 */
	wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;	/* 自定义缓冲区 */
	wanted_spec.callback = ACallback;				/* 回调函数 */
	wanted_spec.userdata = m_avParam;					/* 回调参数 */

	/* 获取并打开音频播放设备 */
#if 1
	int num = SDL_GetNumAudioDevices(0);
	for(int i = 0; i < num; i++)
	{
		m_aDeviceID = SDL_OpenAudioDevice(
					SDL_GetAudioDeviceName(i, 0), false, &wanted_spec, &spec, 0);
		qDebug() << __FUNCTION__ << m_aDeviceID << SDL_GetAudioDeviceName(i, 0)
				 << QThread::currentThreadId();
		if (m_aDeviceID > 0) break;
	}
#else
	m_aDeviceID = SDL_OpenAudioDevice(nullptr, false, &wanted_spec, &spec, SDL_AUDIO_ALLOW_ANY_CHANGE);
	qDebug() << __FUNCTION__ << m_aDeviceID << QThread::currentThreadId();
#endif
	if (m_aDeviceID <= 0)
		return false;

	/* 检查实际使用的配置（保存在spec,由SDL_OpenAudio()填充） */
	if (spec.channels != wanted_spec.channels)
	{	/* 如果双通道不行，则使用实际配置 */
		wanted_channel_layout = av_get_default_channel_layout(spec.channels);
		if (!wanted_channel_layout)
		{
			fprintf(stderr,"SDL advised channel count %d is not supported!\n",spec.channels);
			return false;
		}
	}

	/* 把设置好的参数保存到大结构中 */
	m_avParam->audio_hw_buf_size = spec.size;
	m_avParam->audio_src_fmt = m_avParam->audio_tgt_fmt = AV_SAMPLE_FMT_S16;
	m_avParam->audio_src_freq = m_avParam->audio_tgt_freq = spec.freq;
	m_avParam->audio_src_channel_layout = m_avParam->audio_tgt_channel_layout =
			wanted_channel_layout;
	m_avParam->audio_src_channels = m_avParam->audio_tgt_channels = spec.channels;

	m_avParam->audio_buf_size = 0;
	m_avParam->audio_buf_index = 0;
	memset(&m_avParam->m_aPacket, 0, sizeof(m_avParam->m_aPacket));

	/* 播放音频 */
	SDL_LockAudioDevice(m_aDeviceID);
	SDL_PauseAudioDevice(m_aDeviceID,0);
	SDL_UnlockAudioDevice(m_aDeviceID);

	return true;
}

/* SDL关闭音频设备 */
void QLsAVPlayer::SDLCloseADevice()
{
	if(m_aDeviceID > 0)
	{
		SDL_LockAudioDevice(m_aDeviceID);
		SDL_PauseAudioDevice(m_aDeviceID, 1);
		SDL_UnlockAudioDevice(m_aDeviceID);

		SDL_CloseAudioDevice(m_aDeviceID);
	}

	m_aDeviceID = -1;
}

/* 打开要解码的音视频流 */
bool QLsAVPlayer::AVOpenInput(const QString &url)
{
	m_avParam->m_inputCtx = avformat_alloc_context();

	/* 在打开码流前指定各种参数 */
	AVDictionary* opts = nullptr;
	av_dict_set(&opts, "rtsp_transport", "tcp", 0);		/* 设置tcp or udp，默认一般优先tcp再尝试udp */
	av_dict_set(&opts, "stimeout", "3000000", 0);		/* 设置超时3秒 */
	av_dict_set(&opts, "max_delay", "1000000", 0);		/* 设置最大延时1s */
//	av_dict_set(&opts, "buffer_size", "8192000", 0);	/* 设置缓存大小 */
	av_dict_set(&opts, "threads", "auto", 0);			/* 自动开启线程数 */

	if(avformat_open_input(&m_avParam->m_inputCtx, url.toUtf8().data(), nullptr, &opts))
	{ qDebug() << "打开视频流失败";  return false; }

	/* 释放设置参数 */
	if (opts != NULL) av_dict_free(&opts);

	if(avformat_find_stream_info(m_avParam->m_inputCtx, nullptr) < 0)
	{ qDebug() << "获取文件信息失败";  return false; }
	return true;
}

/* 获取音视频流的位置信息 */
void QLsAVPlayer::AVGetStreamIndex()
{
	m_avParam->m_vStreamIndex = -1;		/* 视频流位置 */
	m_avParam->m_aStreamIndex = -1;		/* 音频流位置 */
	for(int i = 0; i < (int)m_avParam->m_inputCtx->nb_streams; i++)
	{	/* 都获取到则跳出 */
		if(m_avParam->m_vStreamIndex != -1 && m_avParam->m_aStreamIndex != -1) break;
		/* 获取视频流位置 */
		if(m_avParam->m_inputCtx->streams[i]->codecpar->codec_type ==
				AVMEDIA_TYPE_VIDEO && m_avParam->m_vStreamIndex < 0)
			m_avParam->m_vStreamIndex = i;
		/* 获取音频流位置 */
		if(m_avParam->m_inputCtx->streams[i]->codecpar->codec_type ==
				AVMEDIA_TYPE_AUDIO && m_avParam->m_aStreamIndex < 0)
			m_avParam->m_aStreamIndex = i;
	}
	if(m_avParam->m_vStreamIndex == -1)
	{ qDebug() << "没找到视频流"; }
	if(m_avParam->m_aStreamIndex == -1)
	{ qDebug() << "没找到音频流"; }
}

/* 获取音频流信息 */
bool QLsAVPlayer::AVGetAStreamParam()
{
	if(m_avParam->m_aStreamIndex < 0) return true;

	/* 获取音频流 */
	m_avParam->m_aStream = m_avParam->m_inputCtx->streams[m_avParam->m_aStreamIndex];
	m_avParam->m_aStream->discard = AVDISCARD_DEFAULT;		/* 丢弃无用的数据包 */

	/* 查找音频流解码器 */
	m_avParam->m_aCodecCtx = avcodec_alloc_context3(nullptr);
	avcodec_parameters_to_context(m_avParam->m_aCodecCtx, m_avParam->m_aStream->codecpar);
	m_avParam->m_aCodec = avcodec_find_decoder(m_avParam->m_aStream->codecpar->codec_id);
	/**
	 * 2021-05-14 10:25：Could not update timestamps for skipped samples.
	 * 没有此句会出现上述警告。
	 */
	m_avParam->m_aCodecCtx->pkt_timebase = m_avParam->m_aStream->time_base;
	if(!m_avParam->m_aCodec)
	{ qDebug() << "没有查到音频解码器"; return false; }

	/* 打开音频流解码器 */
	if(avcodec_open2(m_avParam->m_aCodecCtx, m_avParam->m_aCodec, nullptr) < 0)
	{ qDebug() << "打开音频解码器失败"; return false; }

	/* 音频流解码包缓冲区队列初始化 */
	m_avParam->m_aPktQueue = new QLsAVPacketQueue;

	/* 解码出的音频数据 */
	m_avParam->m_aFrame = av_frame_alloc();

	/* ----- 重采样设置选项 ----- */
	m_avParam->in_sample_fmt = m_avParam->m_aCodecCtx->sample_fmt;		/* 输入的采样格式 */
	m_avParam->out_sample_fmt = AV_SAMPLE_FMT_S16;						/* 输出的采样格式 16bit PCM */
	m_avParam->in_sample_rate = m_avParam->m_aCodecCtx->sample_rate;	/* 输入的采样率 */
	m_avParam->out_sample_rate = 44100;									/* 输出的采样率 */
	m_avParam->in_ch_layout = m_avParam->m_aCodecCtx->channel_layout;	/* 输入的声道布局 */
	m_avParam->out_ch_layout = av_get_default_channel_layout(2);		/* 输出的声道布局 */
	m_avParam->out_ch_layout &= ~AV_CH_LAYOUT_STEREO_DOWNMIX;
	/* wav/wmv 兼容 */
	if(m_avParam->in_ch_layout <= 0)
		m_avParam->in_ch_layout = av_get_default_channel_layout(
					m_avParam->m_aCodecCtx->channels);
	/* 音频播放参数固定 */
	m_avParam->m_aSwrCtx = swr_alloc();
	m_avParam->m_aSwrCtx = swr_alloc_set_opts(
				m_avParam->m_aSwrCtx,
				m_avParam->out_ch_layout, m_avParam->out_sample_fmt, m_avParam->out_sample_rate,
				m_avParam->in_ch_layout, m_avParam->in_sample_fmt, m_avParam->in_sample_rate,
				0, nullptr);

	/* 使用指定的参数打开重采样器 */
	int ret = swr_init(m_avParam->m_aSwrCtx);
	if (ret < 0)
	{
		char buff[128]={0};
		av_strerror(ret, buff, 128);

		qDebug() << __FUNCTION__ << "Could not open resample context" << buff;
		swr_free(&m_avParam->m_aSwrCtx);
		m_avParam->m_aSwrCtx = nullptr;
		return false;
	}

	/* SDL：打开音频设备、启动音频处理线程、开始播放 */
	if(!SDLOpenADevice())
	{ m_avParam->m_bADecodeThreadFinished = true; }

	return true;
}

/* 获取视频流信息 */
bool QLsAVPlayer::AVGetVStreamParam()
{
	if(m_avParam->m_vStreamIndex < 0) return false;

	/* 获取视频流 */
	m_avParam->m_vStream = m_avParam->m_inputCtx->streams[m_avParam->m_vStreamIndex];

	/* 查找视频流解码器 */
	m_avParam->m_vCodecCtx = avcodec_alloc_context3(nullptr);
	avcodec_parameters_to_context(m_avParam->m_vCodecCtx, m_avParam->m_vStream->codecpar);
	m_avParam->m_vCodec = avcodec_find_decoder(m_avParam->m_vCodecCtx->codec_id);
	if(!m_avParam->m_vCodec)
	{ qDebug() << "没有查到视频解码器"; return false; }
	/* 设置加速解码 */
	m_avParam->m_vCodecCtx->lowres = m_avParam->m_vCodec->max_lowres;
	m_avParam->m_vCodecCtx->flags2 |= AV_CODEC_FLAG2_FAST;

	/* 打开视频流解码器 */
	if(avcodec_open2(m_avParam->m_vCodecCtx, m_avParam->m_vCodec, nullptr) < 0)
	{ qDebug() << "打开视频解码器失败"; return false; }

	/* 视频流解码包缓冲区队列初始化 */
	m_avParam->m_vPktQueue = new QLsAVPacketQueue;

	/* 创建视频解码线程 */
	m_avParam->m_vDecodeThread = QtConcurrent::run(VDecodeThread, m_avParam);

	return true;
}

/* 解码跳转 */
void QLsAVPlayer::AVDecodeSeek()
{
	if(!m_avParam->m_bSeek) return;

	/* 获取并计算时间戳 */
	int stream_index = -1;
	qint64 timestamp = m_avParam->m_seekPos;

	if (m_avParam->m_vStreamIndex >= 0)
		stream_index = m_avParam->m_vStreamIndex;
	else if (m_avParam->m_aStreamIndex >= 0)
		stream_index = m_avParam->m_aStreamIndex;

	AVRational aVRational = {1, AV_TIME_BASE};
	if (stream_index >= 0) {
		timestamp = av_rescale_q(
					timestamp, aVRational,
					m_avParam->m_inputCtx->streams[stream_index]->time_base);
	}

	/* 定位时间戳附近的关键帧 */
	if (av_seek_frame(m_avParam->m_inputCtx, stream_index, timestamp,
					  AVSEEK_FLAG_BACKWARD) < 0)
		qDebug() << m_avParam->m_inputCtx->url << ": error while seeking";
	/* 定位到关键帧了，清空之前的数据包缓存 */
	else
	{	/* 添加一个带有清空数据包缓存命令的数据包 */
		if (m_avParam->m_aStreamIndex >= 0)
		{
			AVPacket *packet = (AVPacket *)malloc(sizeof(AVPacket));
			av_new_packet(packet, 10);
			strcpy((char*)packet->data,FLUSH_DATA);
			m_avParam->m_aPktQueue->Clear();
			m_avParam->m_aPktQueue->Enqueue(packet);
		}
		if (m_avParam->m_vStreamIndex >= 0)
		{
			AVPacket *packet = (AVPacket *) malloc(sizeof(AVPacket));
			av_new_packet(packet, 10);
			strcpy((char*)packet->data,FLUSH_DATA);
			m_avParam->m_vPktQueue->Clear();
			m_avParam->m_vPktQueue->Enqueue(packet);
			m_avParam->m_vClock = 0;
		}

		m_avParam->m_startTime = av_gettime() - m_avParam->m_seekPos;
		m_avParam->m_pauseStartTime = av_gettime();
	}
	m_avParam->m_bSeek = false;
	m_avParam->m_seekTime = m_avParam->m_seekPos / 1e6;
	m_avParam->m_bASeek = true;
	m_avParam->m_bVSeek = true;
	m_avParam->m_bDecodeFinished = false;
}

/* 解码 */
void QLsAVPlayer::Decoding()
{
	/* 存放读取的数据包 */
	AVPacket *packet = (AVPacket *)malloc(sizeof(AVPacket));
	av_init_packet(packet);

	/* 播放状态初始化 */
	m_playerStatus = Player_Playing;
	emit sigStatusChange(Player_Playing);
	m_avParam->m_startTime = av_gettime();

	/* 循环读取数据包 */
	for(;;)
	{
		/* 停止 */
		if (m_avParam->m_bQuit) break;

		/* 跳转 */
		if(m_avParam->m_bSeek)
			AVDecodeSeek();

		/* 数据包队列 上限 */
		if(m_avParam->m_aPktQueue->m_size > MAX_AUDIO_SIZE ||
				m_avParam->m_vPktQueue->m_size > MAX_VIDEO_SIZE)
		{ SDL_Delay(10); continue; }

		/* 暂停 */
		if(m_avParam->m_bPause == true)
		{ SDL_Delay(10); continue; }

		/* 获取流的下一帧(未解码)数据 */
		if(av_read_frame(m_avParam->m_inputCtx, packet) < 0)
		{
			m_avParam->m_bDecodeFinished = true;

			/**
			 * 音频线程结束不会将停止状态置位，视频线程结束才会将停止状态置位
			 * 音视频播放情况下，就算音频结束了，也要等视频结束，将停止状态置位，才是真正的结束，所以继续延时等待
			 * 只有音频的情况下，音频结束，整个工作也就结束了，所以下面要单独判断一下
			 */
			if(m_avParam->m_aStreamIndex >= 0 && m_avParam->m_vStreamIndex < 0 &&
					m_avParam->m_aPktQueue->m_size == 0)
			{
				m_avParam->m_bQuit = true;
				break;
			}

			/**
			 * 解码完毕，但播放没结束，这个时候用户可能会有其他操作
			 * 比如拖进度条等，所以不能直接 break
			 * 这里的 continue 主要为了 seek 服务
			 */
			SDL_Delay(10);
			continue;
		}

		/* 这里我们将数据存入队列 因此不调用 av_packet_unref 释放 */
		/* 视频数据包 */
		if (packet->stream_index == m_avParam->m_vStreamIndex)
			m_avParam->m_vPktQueue->Enqueue(packet);
		/* 音频数据包 */
		else if( packet->stream_index == m_avParam->m_aStreamIndex)
		{	/* SDL打开失败，则直接释放 */
			if(m_avParam->m_bADecodeThreadFinished)
				av_packet_unref(packet);
			else m_avParam->m_aPktQueue->Enqueue(packet);
		}
		/* 其他，直接释放 */
		else av_packet_unref(packet);
	}

	free(packet);
}

/* 解码 的槽函数 */
void QLsAVPlayer::slotLoad()
{
	/* 解码开始 */
	DecodeStart();

	/* 打开要解码的音视频流 */
	if(!AVOpenInput(m_AVUrl)) return;

	/* 获取音视频流的位置信息 */
	AVGetStreamIndex();

	/* 视频总时长加载 */
	emit sigTotalTimeChange(totalTime());

	/* 获取音视频流信息 */
	if(!AVGetAStreamParam()) return;
	if(!AVGetVStreamParam()) return;

	/* 解码 */
	Decoding();

	/* 等待播放完毕 */
	while (!m_avParam->m_bQuit) SDL_Delay(100);

	/* 解码结束 */
	DecodeEnd();
}
